/*
 * Copyright 2006-2007 S W McIlroy
 * Licensed  under the  Apache License,  Version 2.0  (the "License");
 * you may not use  this file  except in  compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed  under the  License is distributed on an "AS IS" BASIS,
 * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
 * implied.
 *
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.mockfu;

import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.ExecutionException;

/**
 * Utilities for managing checked exceptions in an unchecked way.
 */
public class Exceptions
{
    private Exceptions()
    {
    }

    /**
     * Declares an exception so that it can be explicitly caught from a block of code in which it
     * isn't declared. For example, the following code throws an <tt>SQLException</tt>, but
     * wouldn't compile without the call to
     * <tt>{@link #declare(Class)} because <tt>SQLException</tt> is
     * otherwise undeclared.
     *
     * <pre>
     * MockControl&lt;Object&gt; obj = MockControl.of(Object.class);
     * try
     * {
     *     obj.expect().toString();
     *     obj.last().stubException(new SQLException(&quot;mabuse&quot;));
     *     obj.mock().toString();
     *     declare(SQLException.class);
     * }
     * catch (SQLException ex)
     * {
     *     assertThat(ex.getMessage(), &quot;mabuse&quot;);
     * }
     * </pre>
     *
     * By adding a call to <code>declare(SQLException.class)</code> at the bottom of the try block
     * we can in a nice, readable way tell the compiler that, yes, we really do want to catch this
     * exception.
     *
     * @param <T>
     * @param exceptionType the exception type to declare
     * @throws T
     */
    public static <T extends Throwable> void declare(final Class<T> exceptionType) throws T
    {
    }

    /**
     * Throws an exception (checked or unchecked) without declaring it. The RuntimeException return
     * value is simply to enable the following syntax:
     *
     * <pre>
     * throw undeclared(checkedException);
     * </pre>
     *
     * @param ex the exception to throw
     * @return nothing (always throws exception)
     */
    public static RuntimeException undeclared(final Throwable ex)
    {
        if (ex instanceof RuntimeException)
            throw (RuntimeException)ex;
        if (ex instanceof InvocationTargetException || ex instanceof ExecutionException
                || ex instanceof ExceptionInInitializerError)
            throw undeclared(ex.getCause());

        /* for checked exceptions create a proxy and throw from interceptor */
        ProxyBuilder<Object> proxy = new ProxyBuilder<Object>(Object.class, new MethodInterceptor()
        {
            public Object intercept(InvocationContext context) throws Throwable
            {
                throw ex;
            }
        });
        Object exceptionThrower = proxy.create();
        exceptionThrower.toString();
        throw null;
    }

}
